package com.edfu.core.testmgt.persistence.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;

import com.edfu.core.commons.persistence.entity.CategoryEntity;
import com.edfu.core.identitymgt.persistence.entity.UserEntity;
import com.edfu.core.testmgt.persistence.api.ITestMgtPersistence;
import com.edfu.core.testmgt.persistence.entity.AnswersEntity;
import com.edfu.core.testmgt.persistence.entity.FormatTypeEntity;
import com.edfu.core.testmgt.persistence.entity.ImageEntity;
import com.edfu.core.testmgt.persistence.entity.OptionEntity;
import com.edfu.core.testmgt.persistence.entity.QuestionEntity;
import com.edfu.core.testmgt.persistence.entity.QuestionTypeEntity;
import com.edfu.core.testmgt.persistence.entity.TestBundleEntity;
import com.edfu.core.testmgt.persistence.entity.TestEntity;
import com.edfu.core.testmgt.persistence.entity.TestTypeEntity;
import com.edfu.core.testmgt.persistence.entity.UserTestAnswersEntity;
import com.edfu.core.testmgt.persistence.entity.UserTestSubscriptionEntity;

public class TestMgtPersistenceImpl implements ITestMgtPersistence {
	
	private static Logger logger = LoggerFactory.getLogger(TestMgtPersistenceImpl.class);
	
	private EntityManager entityManager;
	
	@Autowired
	private MongoTemplate mongoTemplate;
	
	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	@Override
	public TestEntity createTest(TestEntity te) {
		logger.debug("Entered [createTest] of [TestMgtPersistenceImpl]");
		return entityManager.merge(te);
	}

	@Override
	public TestEntity updateTest(TestEntity te) {
		return entityManager.merge(te);
	}

	@Override
	public void removeTest(TestEntity te) {
		te.setActive(false);
		entityManager.persist(te);
	}

	@Override
	public TestEntity getTest(long testId) {
		return entityManager.find(TestEntity.class, testId);
	}

	@SuppressWarnings("unchecked")
	@Override
	public TestEntity getTest(String testCode) {
		Query query =  entityManager.createQuery("select t from TestEntity t where t.code = :testCode and t.active = true");
		query.setParameter("testCode", testCode);
		
		List<TestEntity> entityList = query.getResultList();
		TestEntity te = null;
		
		if (entityList != null && entityList.size() > 0) {
			te = entityList.get(0);
		}
		
		return te;
	}

	@Override
	public boolean isTestUnique(String testCode) {
		return getTest(testCode) != null ? false : true;
	}

	@Override
	public TestBundleEntity createTestBundle(TestBundleEntity testBundle) {
		return entityManager.merge(testBundle);
	}

	@Override
	public TestBundleEntity updateTestBundle(TestBundleEntity testBundle) {
		return entityManager.merge(testBundle);
	}

	@Override
	public void removeTestBundle(TestBundleEntity testBundle) {
		testBundle.setActive(false);
		
		entityManager.persist(testBundle);
	}

	@Override
	public TestBundleEntity getTestBundle(long testBundleId) {
		return entityManager.find(TestBundleEntity.class, testBundleId);
	}

	@SuppressWarnings("unchecked")
	@Override
	public TestBundleEntity getTestBundle(String testBundleCode) {
		Query query = entityManager.createQuery("select t from TestBundleEntity t where t.code = :testBundleCode and t.active = true");
		query.setParameter("testBundleCode", testBundleCode);
		
		List<TestBundleEntity> entityList = query.getResultList();
		TestBundleEntity tbe = null;
		
		if (entityList != null && entityList.size() > 0) {
			tbe = entityList.get(0);
		}
		
		return tbe;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TestEntity> getTests(TestBundleEntity testBundle) {
		Query query = entityManager.createQuery("select t from TestEntity t where t.testBundle = :testBundle and t.active = true");
		query.setParameter("testBundle", testBundle);
		
		return query.getResultList();
	}

	@Override
	public boolean isTestBundleUnique(String testBundleCode) {
		return getTestBundle(testBundleCode) != null ? false : true;
	}

	@Override
	public TestTypeEntity createTestType(String testTypeName, String testTypeDesc) {
		TestTypeEntity tte = new TestTypeEntity();
		tte.setName(testTypeName);
		tte.setDescription(testTypeDesc);
		
		return entityManager.merge(tte);
	}

	@Override
	public TestTypeEntity updateTestType(TestTypeEntity testType) {
		return entityManager.merge(testType);
	}

	@Override
	public void removeTestType(TestTypeEntity testType) {
		entityManager.remove(testType);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TestTypeEntity> getTestTypes() {
		Query query = entityManager.createQuery("select t from TestTypeEntity t");		
		
		return query.getResultList();
	}

	@Override
	public TestTypeEntity getTestType(long testTypeId) {
		return entityManager.find(TestTypeEntity.class, testTypeId);
	}

	@SuppressWarnings("unchecked")
	@Override
	public TestTypeEntity getTestType(String testTypeName) {
		Query query = entityManager.createQuery("select t from TestTypeEntity t where t.name = :testTypeName");
		query.setParameter("testTypeName", testTypeName);
		List<TestTypeEntity> entityList = query.getResultList();
		TestTypeEntity tte = null;
		
		if (entityList != null && entityList.size() > 0) {
			tte = entityList.get(0);
		}
		
		return tte;	
	}

	@Override
	public boolean isTestTypeUnique(String testTypeName) {
		return getTestType(testTypeName) != null ? false : true;
	}

	@Override
	public QuestionEntity addQuestion(TestEntity te, QuestionEntity qe) {
		qe = entityManager.merge(qe);
		
		te.getQuestions().add(qe);
		entityManager.merge(te);;
		
		return qe;
	}

	@Override
	public boolean isQuestionUnique(String questionCode) {
		return getQuestion(questionCode) != null ? false : true;
	}

	@Override
	public QuestionEntity updateQuestion(QuestionEntity question) {
		return entityManager.merge(question);
	}

	@Override
	public void removeQuestion(QuestionEntity question) {
		entityManager.remove(question);
	}

	@Override
	public QuestionEntity getQuestion(long id) {
		return entityManager.find(QuestionEntity.class, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public QuestionEntity getQuestion(String questionCode) {
		Query query = entityManager.createQuery("select q from QuestionEntity q where q.code = :questionCode and q.active = true");
		query.setParameter("questionCode", questionCode);
		
		List<QuestionEntity> entityList = query.getResultList();
		QuestionEntity qe = null;
		
		if (entityList != null && entityList.size() > 0) {
			qe = entityList.get(0);
		}
		
		return qe;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Set<OptionEntity> getAnswers(QuestionEntity question) {
		Query query = entityManager.createQuery("Select t from AnswersEntity t where t.question.id = :questionId");
		query.setParameter("questionId", question.getId());
		
		List<OptionEntity> optionList = query.getResultList();
		Set<OptionEntity> optionSet = new HashSet<OptionEntity>();
		
		for (OptionEntity oe : optionList) {
			optionSet.add(oe);
		}
		
		return optionSet;
	}

	@Override
	public QuestionTypeEntity createQuestionType(String questionTypeName, String questionTypeDesc) {
		QuestionTypeEntity qte = new QuestionTypeEntity();
		qte.setName(questionTypeName);
		qte.setDescription(questionTypeDesc);
		
		return entityManager.merge(qte);
	}

	@Override
	public QuestionTypeEntity updateQuestionType(QuestionTypeEntity questionType) {
		return entityManager.merge(questionType);
	}

	@Override
	public void removeQuestionType(QuestionTypeEntity questionType) {
		entityManager.remove(questionType);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<QuestionTypeEntity> getQuestionTypes() {
		Query query = entityManager.createQuery("select q from QuestionTypeEntity q");		
		
		return query.getResultList();
	}

	@Override
	public QuestionTypeEntity getQuestionType(long questionTypeId) {
		return entityManager.find(QuestionTypeEntity.class, questionTypeId);
	}

	@SuppressWarnings("unchecked")
	@Override
	public QuestionTypeEntity getQuestionType(String questionTypeName) {
		Query query = entityManager.createQuery("select q from QuestionTypeEntity q where q.name = :questionTypeName");
		query.setParameter("questionTypeName", questionTypeName);
		
		List<QuestionTypeEntity> entityList = query.getResultList();
		QuestionTypeEntity qte = null;
		
		if (entityList != null && entityList.size() > 0) {
			qte = entityList.get(0);
		}
		
		return qte;
	}

	@Override
	public boolean isQuestionTypeUnique(String questionTypeName) {
		return getQuestionType(questionTypeName) != null ? false : true;
	}

	@Override
	public OptionEntity getOption(long optionId) {
		return entityManager.find(OptionEntity.class, optionId);
	}

	@Override
	public void subscribeTest(UserTestSubscriptionEntity userTestSubscription) {
		userTestSubscription.setUnsubscribe(false);
		
		entityManager.persist(userTestSubscription);
	}
	
	@Override
	public UserTestSubscriptionEntity updateUserTestSubscription(UserTestSubscriptionEntity userTestSubscription) {
		return entityManager.merge(userTestSubscription);
	}

	@Override
	public void unsubscribeTest(UserTestSubscriptionEntity userTestSubscription) {
		userTestSubscription.setUnsubscribe(true);
		
		entityManager.persist(userTestSubscription);
	}

	@Override
	public UserTestSubscriptionEntity addOrUpdateUserTestDetails(UserTestSubscriptionEntity userTest) {
		return entityManager.merge(userTest);
	}

	@Override
	public void submitUserTestAnswers(UserTestAnswersEntity userTestAnswers) {
		entityManager.persist(userTestAnswers);
		
//		mongoTemplate.upsert(new org.springframework.data.mongodb.core.query.Query(Criteria.where("userId").is(userTestAnswers.getUser().getUserId())).addCriteria(Criteria.where("testId").is(userTestAnswers.getTest().getId())), new Update().addToSet(String.valueOf(userTestAnswers.getQuestion().getId()), userTestAnswers.getOption().getId()), UserTestAnswersEntity.class);
		
	}

	@SuppressWarnings("unchecked")
	@Override
	public UserTestSubscriptionEntity getUserTestSubscriptionEntity(UserEntity ue, TestEntity te, long testDate) {
		Query query = entityManager.createQuery("select u from UserTestSubscriptionEntity u where u.user = :user and u.test = :test and u.testDate = :testDate");
		query.setParameter("user", ue);
		query.setParameter("test", te);
		query.setParameter("testDate", testDate);
		
		List<UserTestSubscriptionEntity> entityList = query.getResultList();
		UserTestSubscriptionEntity utse = null;
		
		if (entityList != null && entityList.size() > 0) {
			utse = entityList.get(0);
		}
		
		return utse;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<UserTestAnswersEntity> getUserTestAnswers(TestEntity te, UserEntity ue, long testDate) {
		Query query = entityManager.createQuery("select u from UserTestAnswersEntity u where u.user = :user and u.test = :test and u.testDate = :testDate");
		query.setParameter("user", ue);
		query.setParameter("test", te);
		query.setParameter("testDate", testDate);
		
		List<UserTestAnswersEntity> entityList = query.getResultList();
		
		return entityList;
	}

	@Override
	public FormatTypeEntity createFormatType(String formatType) {
		FormatTypeEntity fte = new FormatTypeEntity();
		fte.setType(formatType);
		
		return entityManager.merge(fte);
	}

	@Override
	public FormatTypeEntity updateFormatType(FormatTypeEntity formatType) {
		return entityManager.merge(formatType);
	}

	@Override
	public void removeFormatType(FormatTypeEntity formatType) {
		entityManager.remove(formatType);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<FormatTypeEntity> getFormatTypes() {
		Query query = entityManager.createQuery("select f from FormatTypeEntity f");		
		
		return query.getResultList();
	}

	@Override
	public FormatTypeEntity getFormatType(long formatTypeId) {
		return entityManager.find(FormatTypeEntity.class, formatTypeId);
	}

	@SuppressWarnings("unchecked")
	@Override
	public FormatTypeEntity getFormatType(String formatTypeName) {
		Query query = entityManager.createQuery("select f from FormatTypeEntity f where f.type = :formatTypeName");
		query.setParameter("formatTypeName", formatTypeName);
		
		List<FormatTypeEntity> entityList = query.getResultList();
		FormatTypeEntity fte = null;
		
		if (entityList != null && entityList.size() > 0) {
			fte = entityList.get(0);
		}
		
		return fte;
	}

	@Override
	public boolean isFormatTypeUnique(String formatTypeName) {
		return getFormatType(formatTypeName) != null ? false : true;
	}

	@Override
	public OptionEntity addOption(OptionEntity optEnt) {
		return entityManager.merge(optEnt);
	}
	
	@Override
	public OptionEntity updateOption(OptionEntity optEnt) {
		return entityManager.merge(optEnt);
	}

	@Override
	public AnswersEntity addAnswer(AnswersEntity ansEnt) {
		return entityManager.merge(ansEnt);
	}

	@Override
	public ImageEntity addImage(ImageEntity imageEntity) {
		return entityManager.merge(imageEntity);
	}

	@Override
	public ImageEntity updateImage(ImageEntity imageEntity) {
		return entityManager.merge(imageEntity);
	}

	@Override
	public void deleteImage(ImageEntity imageEntity) {
		entityManager.remove(imageEntity);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<ImageEntity> getImages(QuestionEntity questionEntity) {
		Query query = entityManager.createQuery("select i from ImageEntity i where i.question = :question");
		query.setParameter("question", questionEntity);
		
		List<ImageEntity> entityList = query.getResultList();
		
		return entityList;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ImageEntity getImage(TestEntity testEntity, String fileName) {
		Query query = entityManager.createQuery("select i from ImageEntity i where i.test = :test");
		query.setParameter("test", testEntity);
		
		List<ImageEntity> entityList = query.getResultList();
		ImageEntity ie = null;
		if (entityList != null && entityList.size() > 0) {
			ie = entityList.get(0);
		}
		
		return ie;
	}

	@SuppressWarnings("unchecked")
	@Override
	public ImageEntity getImage(QuestionEntity questionEntity, OptionEntity optionEntity, String name) {
		Query query = null;
		
		if (questionEntity != null && optionEntity != null) {
			query = entityManager.createQuery("select i from ImageEntity i where i.question = :question and i.option = :option and i.name = :name");
			query.setParameter("question", questionEntity);
			query.setParameter("option", optionEntity);
			query.setParameter("name", name);
		} else if(questionEntity != null && optionEntity == null) {
			query = entityManager.createQuery("select i from ImageEntity i where i.question = :question and i.name = :name");
			query.setParameter("question", questionEntity);
			query.setParameter("name", name);
		}
		
		List<ImageEntity> entityList = query.getResultList();
		ImageEntity ie = null;
		if (entityList != null && entityList.size() > 0) {
			ie = entityList.get(0);
		}
		
		return ie;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TestEntity> getTestDetails() {
		Query query = entityManager.createQuery("select t from TestEntity t");
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TestEntity> getSubscribedTests(UserEntity userEntity) {
		Query query = entityManager.createQuery("select t from UserTestSubscriptionEntity t where t.user = :user");
		query.setParameter("user", userEntity);
		
		return query.getResultList();
	}

	@Override
	public TestEntity associateTestToCategory(TestEntity tce) {
		return entityManager.merge(tce);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<TestEntity> getAssociatedTests(CategoryEntity cce) {
		Query query = entityManager.createQuery("select t from TestEntity t where t.category = :category");
		query.setParameter("category", cce);
		
		return query.getResultList();
	}	
	

}
